package com.webserver.http;
import static com.webserver.http.HttpContext.CR;//静态导入一个类的静态属性
import static com.webserver.http.HttpContext.LF;
import static com.webserver.http.HttpContext.getMimeType;//静态导入一个类的静态方法

import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 响应对象
 * 该类的每一个实例用于表示HTTP协议规定的一个响应内容.
 * 每个响应都由三部分构成:
 * 状态行,响应头,响应正文
 *
 * 第三步:
 * 将response()方法中三个发送步骤定义为三个方法,并分别调用使得代码的可读性更好
 * 为状态代码和描述设定默认值:200,OK  目的:因为实际应用中大部分响应都是成功的,没必要每次都设置200
 */
public class HttpServletResponse {
    //状态行相关信息
    private int statusCode = 200;//状态代码
    private String statusReason = "OK";//状态描述

    //响应头相关信息
    //key:响应头的名字  value:响应头对应的值
    private Map<String,String> headers = new HashMap<>();

    //响应正文相关信息
    private File contentFile;//响应正文对应的文件,该文件数据会作为正文内容发送

    //通常存的是动态数据对应的一组字节
    private byte[] contentData;//一组字节作为正文内容

    /*
        java.io.ByteArrayOutputStream
        该流是一个字节流，且是一个低级流。内部维护一个字节数组，通过这个流写出的数据全部
        会保存到内部的字节数组中。
     */
    private ByteArrayOutputStream out;

    private Socket socket;

    public HttpServletResponse(Socket socket){
        this.socket = socket;
    }

    /**
     * 发送响应
     * 将当前响应对象内容按照标准的HTTP响应格式发送给客户端
     */
    public void response() throws IOException {
        //发送响应前的准备
        sendBefore();
        //3.1发送状态行
        sendStatusLine();
        //3.2发送响应头
        sendHeaders();
        //3.3发送响应正文
        sendContent();
        System.out.println("响应发送完毕!");
    }
    //发送响应前的准备工作
    private void sendBefore(){
        if(out != null){//如果ByteArrayOutputStream不为空，说明获取过这个流写了动态数据
            contentData = out.toByteArray();//将输出流内部的字节数组获取出来作为正文数据
        }
        if(contentData != null){//若contentData不为空，说明是有一组字节作为正文要发送
            //根据该字节数组的长度设置Content-Length
            addHeader("Content-Length",contentData.length+"");
        }
    }

    //发送状态行
    private void sendStatusLine() throws IOException {
        String line = "HTTP/1.1"+" "+statusCode+" "+statusReason;
        println(line);
    }
    //发送响应头
    private void sendHeaders() throws IOException {
        Set<Map.Entry<String,String>> headerSet = headers.entrySet();
        for(Map.Entry<String,String> e : headerSet){
            String key = e.getKey();
            String value = e.getValue();
            println(key+": "+value);
        }
        println("");//单独发送回车换行表示响应头发送完毕
    }
    //发送响应正文
    private void sendContent() throws IOException {
        OutputStream out = socket.getOutputStream();
        if(contentData != null){
            out.write(contentData);//将contentData中的所有字节作为正文发送给浏览器
        }else if(contentFile!=null) {
            try (
                    FileInputStream fis = new FileInputStream(contentFile);
            ) {
                byte[] data = new byte[1024 * 10];
                int len;
                while ((len = fis.read(data)) != -1) {
                    out.write(data, 0, len);
                }
            }
        }
    }




    private void println(String line) throws IOException {
        //通过socket获取输出流,用于给浏览器发送数据
        OutputStream out = socket.getOutputStream();
        //将要发送的字符串使用ISO8859-1编码转换为一组字节
        byte[] data = line.getBytes(StandardCharsets.ISO_8859_1);
        //将这组字节发送给浏览器
        out.write(data);
        out.write(CR);//单独发送回车符
        out.write(LF);//单独发送换行符
    }

    public int getStatusCode() {
        return statusCode;
    }

    public void setStatusCode(int statusCode) {
        this.statusCode = statusCode;
    }

    public String getStatusReason() {
        return statusReason;
    }

    public void setStatusReason(String statusReason) {
        this.statusReason = statusReason;
    }

    public File getContentFile() {
        return contentFile;
    }

    public void setContentFile(File contentFile) {
        this.contentFile = contentFile;
        //添加正文文件的同时，根据该文件添加响应头Content-Type和Content-Length
        String fileName = contentFile.getName();
        String ext = fileName.substring(fileName.lastIndexOf(".")+1);
        addHeader("Content-Type", getMimeType(ext));
        addHeader("Content-Length",contentFile.length()+"");

    }
    /*
        添加一个响应头
     */
    public void addHeader(String name,String value){
        this.headers.put(name,value);
    }

    /**
     * 返回一个字节输出流，通过这个流写出的数据最终都会作为响应正文发送给浏览器
     * @return
     */
    public OutputStream getOutputStream(){
        if(out==null){
            out = new ByteArrayOutputStream();
        }
        return out;
    }

    public PrintWriter getWriter(){
        return new PrintWriter(
                new BufferedWriter(
                        new OutputStreamWriter(
                                getOutputStream(),StandardCharsets.UTF_8
                        )
                ),true
        );
    }

    /**
     * 设置响应头Content-Type
     * @param mime
     */
    public void setContentType(String mime){
        addHeader("Content-Type",mime);
    }

}
